/**
 ** @file       DBFoundation.h
 ** @brief      An Objective-C 2.0 wrapper for SQLite 3.4.0 or later.
 ** @version    1.0
 **
 ** @section    db_license      MIT License
 **
 ** Copyright (c) 2008-2009 Ben Rimmington <http://ben-rimmington.googlecode.com/>
 **
 ** Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
 ** to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
 ** and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 **
 ** The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 **
 ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 ** DEALINGS IN THE SOFTWARE.
 **
 ** @section    db_required     Requirements
 **
 ** - The DBFoundation.h interface file and DBFoundation.m implementation file are required.
 ** - The "libsqlite3.dylib" dynamic library (added via the General tab of a target's Get Info window, in Xcode 3.1 or later) is required.
 ** - The modern version of the Objective-C 2.0 <a href="http://developer.apple.com/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/">runtime</a>
 **   (used by iPhone OS and 64-bit Mac OS X applications) is recommended
 **   (for <a href="http://developer.apple.com/releasenotes/Cocoa/RN-ObjectiveC/">zero-cost</a> \@try blocks).
 ** - Manual memory management (<a href="http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/">reference counting</a>) is supported.
 **   Automatic memory management (<a href="http://developer.apple.com/documentation/Cocoa/Conceptual/GarbageCollection/">garbage collection</a>) is untested.
 ** - The DBStatement#close and DBConnection#close methods must be invoked in sequence (with either memory management system).
 **
 ** @section    db_threadsafe   Thread Safety
 **
 ** - The <a href="http://www.sqlite.org/c3ref/threadsafe.html">sqlite3_threadsafe()</a> function is available in SQLite 3.5.0 or later.
 ** - If the "libsqlite3.dylib" dynamic library was compiled with <a href="http://www.sqlite.org/compile.html#threadsafe">SQLITE_THREADSAFE</a>=1 or 2:
 **     -# multiple threads can share the same DBConnection object (see <a href="http://www.sqlite.org/faq.html#q6">FAQ 6</a> for limitations),
 **     -# each thread can also create its own DBConnection and DBStatement objects (see @ref db_example2).
 ** - DBFoundation isn't thread-safe, so access to a shared DBConnection object must be synchronized externally.
 **
 ** @section    db_collation    Collation Sequences
 **
 ** - The default <a href="http://www.sqlite.org/datatype3.html#collation">collation sequence</a> uses the -[NSString localizedCompare:] method.
 ** - A different comparison method of NSString (or a category thereof) can be specified with the COLLATE keyword.
 ** - The colon suffix of the comparison selector isn't included, otherwise the SQL statement may not compile.
 ** - The table and column names in this example could be improved by adding prefixes, to avoid name-space collisions with SQLite
 **   <a href="http://www.sqlite.org/lang_keywords.html">keywords</a>.
 **
 ** @code
 ** SELECT * FROM Book ORDER BY title COLLATE localizedCaseInsensitiveCompare ASC
 ** @endcode
 **
 ** @section    db_example1     Example 1
 **
 ** In the following example:
 ** - A DBConnection object connects to a temporary in-memory database.
 ** - A DBStatement object compiles a <a href="http://www.sqlite.org/lang_select.html">SELECT</a> statement, and binds the
 **   <a href="http://developer.apple.com/documentation/Darwin/Reference/ManPages/man3/math.3.html">M_PI</a> constant to a $M_PI parameter.
 ** - The fast enumeration (for...in) language feature is used to evaluate a compiled SQL statement.
 ** - SQLite errors are converted into exceptions, so \@try, \@catch and \@finally blocks are defined.
 ** - The DBConnection and DBStatement pointers are initialized to `nil` before entering the \@try block.
 **
 ** @code
 ** #import "DBFoundation.h"
 **
 ** #define DBLog(expression) NSLog(@"DBLog >> %s >> %@", #expression, (expression))
 **
 ** int main(int argc, const char *argv[]) {
 **     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 **     NSString *sql = @"SELECT NULL, 42 AS 'The Ultimate Answer', $M_PI, sqlite_version(), randomblob(8)";
 **     DBConnection *connection = nil;
 **     DBStatement *statement = nil;
 **     @try {
 **         connection = [[DBConnection alloc] initWithPath:@":memory:"];
 **         statement = [[DBStatement alloc] initWithConnection:connection SQL:sql];
 **         [statement setValue:[NSNumber numberWithDouble:M_PI] forKey:@"$M_PI"];
 **         for (id row in statement) {
 **             DBLog([row description]);
 **             DBLog([row valueForKey:@"The Ultimate Answer"]);
 **             DBLog([row valueForKey:@"1"]);
 **             DBLog([[row valueForKey:@"*"] objectAtIndex:1]);
 **         }
 **     } @catch (NSException *exception) {
 **         NSLog(@"%@: %@\nUser Info: %@", [exception name], [exception reason], [exception userInfo]);
 **     } @finally {
 **         [[statement close] release];
 **         [[connection close] release];
 **     }
 **     [pool drain];
 **     return 0;
 ** }
 ** @endcode
 **
 ** @code
 ** DBLog >> [row description] >> {
 **     "$M_PI" = 3.141592653589793;
 **     "*" =     (
 **         <null>,
 **         42,
 **         3.141592653589793,
 **         "3.4.0",
 **         <d891d8e3 95b53699>
 **     );
 **     0 = <null>;
 **     1 = 42;
 **     2 = 3.141592653589793;
 **     3 = "3.4.0";
 **     4 = <d891d8e3 95b53699>;
 **     NULL = <null>;
 **     "The Ultimate Answer" = 42;
 **     "randomblob(8)" = <d891d8e3 95b53699>;
 **     "sqlite_version()" = "3.4.0";
 ** }
 ** DBLog >> [row valueForKey:@"The Ultimate Answer"] >> 42
 ** DBLog >> [row valueForKey:@"1"] >> 42
 ** DBLog >> [[row valueForKey:@"*"] objectAtIndex:1] >> 42
 ** @endcode
 **
 ** @section    db_example2     Example 2
 **
 ** In the following example:
 ** - The methods belong to an NSOperation subclass that conforms to the DBConnectionDelegate protocol.
 ** - The -[NSOperation main] method creates its own DBConnection object (see @ref db_threadsafe).
 ** - The -[NSOperation isCancelled] method is used to decide whether the current database operation should be cancelled.
 ** - The -[DBConnectionDelegate connection:shouldRetryLockWithCount:] method attempts to access a locked database for approximately 10 seconds.
 **
 ** @code
 ** - (void)main {
 **     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 **     DBConnection *connection = nil;
 **     @try {
 **         connection = [[DBConnection alloc] initWithPath:self.path];
 **         connection.delegate = self;
 **         [connection executeSQL:@"BEGIN TRANSACTION"];
 **         // ... //
 **         [connection executeSQL:@"COMMIT TRANSACTION"];
 **     } @catch (NSException *exception) {
 **         NSLog(@"%@: %@\nUser Info: %@", [exception name], [exception reason], [exception userInfo]);
 **         if (connection.withinTransaction) {
 **             [connection executeSQL:@"ROLLBACK TRANSACTION"];
 **         }
 **     } @finally {
 **         [[connection close] release];
 **     }
 **     [pool drain];
 ** }
 **
 ** - (BOOL)connectionShouldInterrupt:(DBConnection *)connection {
 **     return [self isCancelled];
 ** }
 **
 ** - (BOOL)connection:(DBConnection *)connection shouldRetryLockWithCount:(NSUInteger)retryCount {
 **     if ([self isCancelled] || (retryCount > 1e4)) {
 **         return NO;
 **     } else {
 **         [NSThread sleepForTimeInterval:1e-3];
 **         return YES;
 **     }
 ** }
 ** @endcode
 **/

#import <Foundation/Foundation.h>
#import <sqlite3.h>

@class DBFunction;
@class DBConnection;
@class DBStatement;

/// The `name` of an NSException object (with an optional `userInfo` dictionary).
extern NSString *const DBException;

/// The `userInfo` key for an <a href="http://www.sqlite.org/c3ref/errcode.html">sqlite3_errcode()</a> value.
extern NSString *const SQLiteErrorCodeKey;

/// The `userInfo` key for an <a href="http://www.sqlite.org/c3ref/errcode.html">sqlite3_errmsg()</a> value.
extern NSString *const SQLiteErrorMessageKey;

/// The `userInfo` key for an <a href="http://www.sqlite.org/c3ref/result_blob.html">sqlite3_result()</a> value.
extern NSString *const SQLiteResultKey;

/// The `userInfo` key for an <a href="http://www.sqlite.org/c3ref/libversion.html">sqlite3_libversion()</a> value.
extern NSString *const SQLiteVersionKey;

/**
 ** @brief      Required methods of the SQLite <a href="http://www.sqlite.org/datatype3.html">data type</a> wrappers.
 ** @details    The data types used by SQLite are NULL, INTEGER, REAL, TEXT and BLOB. The corresponding wrappers are NSNull, NSNumber, NSString and NSData.
 **             Each wrapper implements the #numberValue, #stringValue and #dataValue methods as follows:
 **             - If no conversion is necessary, the receiver is retained and autoreleased.
 **             - Otherwise, the result is a new (autoreleased) value or `nil`.
 **             - The UTF-8 encoding is used when converting between NSString and NSData values.
 **/
@protocol DBValue <NSObject>

- (void)setResultOfFunctionWithContext:(sqlite3_context *)context;                              // PRIVATE
- (BOOL)bindToStatement:(DBStatement *)statement atParameterIndex:(NSUInteger)parameterIndex;   // PRIVATE

/// A numerical representation or `nil` [readonly].
@property (nonatomic, readonly) NSNumber *numberValue;

/// A textual representation or `nil` [readonly].
@property (nonatomic, readonly) NSString *stringValue;

/// A binary representation or `nil` [readonly].
@property (nonatomic, readonly) NSData *dataValue;

@end

@interface NSNull (DBValue) <DBValue>
@end

@interface NSNumber (DBValue) <DBValue>
@end

@interface NSString (DBValue) <DBValue>
@end

@interface NSData (DBValue) <DBValue>
@end

/**
 ** @brief      Required methods of the DBConnection.delegate object.
 ** @note       The associated DBConnection and DBStatement objects must not be closed during these methods.
 ** @see        @ref db_example2
 **/
@protocol DBConnectionDelegate <NSObject>

/**
 ** @brief      Asks the delegate whether the current database operation should be cancelled.
 ** @return     If the delegate returns YES, the current transaction may be rolled back automatically (an SQLITE_INTERRUPT error).
 ** @param      connection      The delegating DBConnection object.
 **/
- (BOOL)connectionShouldInterrupt:(DBConnection *)connection;

/**
 ** @brief      Asks the delegate whether another attempt should be made to access a locked database.
 ** @return     If the delegate returns NO, the current transaction may be rolled back automatically (an SQLITE_BUSY or SQLITE_IOERR_BLOCKED error).
 ** @param      connection      The delegating DBConnection object.
 ** @param      retryCount      The number of retries (0..n) for this locking event.
 **/
- (BOOL)connection:(DBConnection *)connection shouldRetryLockWithCount:(NSUInteger)retryCount;

@end

/**
 ** @brief      An application-defined SQL function.
 ** @details    Each instance method of DBFunction (or a category thereof) is automatically registered as an SQL function.
 ** @return     The return type of each instance method determines the SQL function type:
 **             - Scalars return `id`, and can be used in any <a href="http://www.sqlite.org/lang_expr.html">SQL expression</a>.
 **             - Aggregates return `void`, and can only be used in <a href="http://www.sqlite.org/lang_select.html">SELECT</a> statements.
 ** @param      userInfo        The first parameter of each instance method is a mutable dictionary:
 **                             - Scalars can store <a href="http://www.sqlite.org/c3ref/get_auxdata.html">metadata</a> (application-defined objects)
 **                               for zero-based argument indices (NSString keys). The metadata for constant arguments (literal values and SQL parameters)
 **                               is retained by SQLite, until the compiled SQL statement has finished executing.
 **                             - Aggregates can store intermediate results (application-defined objects and keys) when invoked for each row of the result set.
 **                               The #SQLiteResultKey is then preset (with a default NSNull value), and the aggregate is invoked again to finalize the result.
 ** @param      ...             The other parameters determine whether the SQL function supports a fixed or varying number of arguments:
 **                             - Fixed -- the instance method has zero or more unlabelled `id` parameters.
 **                             - Varying -- the last parameter is an immutable array, labelled with three underscores.
 **
 ** @code
 ** @implementation DBFunction (Examples)
 ** - (id)last_insert_rowid:(NSMutableDictionary *)userInfo { }                             // last_insert_rowid()
 ** - (id)datetime:(NSMutableDictionary *)userInfo :(id)value ___:(NSArray *)modifiers { }  // datetime(value, ...)
 ** - (id)trim:(NSMutableDictionary *)userInfo :(id)value { }                               // trim(value)
 ** - (id)trim:(NSMutableDictionary *)userInfo :(id)value :(id)characters { }               // trim(value, characters)
 ** - (id)max:(NSMutableDictionary *)userInfo ___:(NSArray *)values { }                     // max(...)
 ** - (void)max:(NSMutableDictionary *)userInfo :(id)value { }                              // max(value)
 ** - (void)count:(NSMutableDictionary *)userInfo { }                                       // count(*)
 ** - (void)count:(NSMutableDictionary *)userInfo :(id)value { }                            // count(value)
 ** @end
 ** @endcode
 **
 ** @par        Notes:
 **             - Valid arguments and results are `nil`, NSNull, NSNumber, NSString and NSData (see the DBValue protocol).
 **             - The beginning of each selector (up to the first colon character) is registered as an SQL function name.
 **             - If an instance method throws an exception, the `reason` is used by SQLite as an error message.
 **             - Aggregates are unable to execute SQL statements (with the receiver's #connection property) in SQLite 3.4.0.
 **             - DBFunction also provides Unicode-based implementations of the `lower` and `upper` core functions.
 ** @see        <a href="http://www.sqlite.org/lang_corefunc.html">Core Functions</a>,
 **             <a href="http://www.sqlite.org/lang_datefunc.html">Date and Time Functions</a>,
 **             <a href="http://www.sqlite.org/lang_aggfunc.html">Aggregate Functions</a>
 **/
@interface DBFunction : NSObject {
@private
    __weak DBConnection *db_connection;
    SEL db_selector;
}

/// A connection to an SQLite database [readonly].
@property (nonatomic, readonly, assign) __weak DBConnection *connection;

@end

/**
 ** @brief      A connection to an SQLite database.
 ** @throw      #DBException if an SQLite error occurs.
 **/
@interface DBConnection : NSObject {
@private
    NSString *db_path;
    sqlite3 *db_handle;
    __weak id <DBConnectionDelegate> db_delegate;
    NSArray *db_functions;
}

/**
 ** @brief      Connects to an SQLite database.
 ** @return     An initialized DBConnection object.
 ** @param      path
 **             - The filename of a new or existing database.
 **             - Or @"" for a temporary on-disk database.
 **             - Or @":memory:" for a temporary in-memory database.
 **/
- (id)initWithPath:(NSString *)path;

/**
 ** @brief      Disconnects from an SQLite database.
 ** @note       The associated DBStatement objects must be closed first.
 ** @return     `self` so that a nested expression can be used (see the \@finally block of @ref db_example1).
 **/
- (id)close;

/**
 ** @brief      Executes an SQL statement, by creating a temporary DBStatement object.
 ** @return     The first column of the first row as a `nil`, NSNull, NSNumber, NSString or NSData value (see the DBValue protocol).
 ** @note       To access other columns or rows, use a fast enumeration instead (see @ref db_example1).
 ** @param      sql             An SQL statement, using the <a href="http://www.sqlite.org/lang.html">language</a> that SQLite understands.
 **
 ** @code
 ** [connection executeSQL:@"PRAGMA auto_vacuum = 1"];
 ** NSNumber *auto_vacuum = [[connection executeSQL:@"PRAGMA auto_vacuum"] numberValue];
 ** @endcode
 **/
- (id)executeSQL:(NSString *)sql;

/// The path used by the initializer [readonly].
@property (nonatomic, readonly, copy) NSString *path;

/// The <a href="http://www.sqlite.org/c3ref/sqlite3.html">sqlite3</a> handle [readonly].
@property (nonatomic, readonly, assign) sqlite3 *handle;

/// An optional delegate, the default is `nil` [readwrite].
@property (nonatomic, readwrite, assign) __weak id <DBConnectionDelegate> delegate;

/// NO if the connection is in <a href="http://www.sqlite.org/c3ref/get_autocommit.html">auto-commit</a> mode [readonly].
@property (nonatomic, readonly) BOOL withinTransaction;

/// The <a href="http://www.sqlite.org/c3ref/last_insert_rowid.html">rowid</a> for the most recent INSERT statement [readonly].
@property (nonatomic, readonly) NSNumber *lastInsertRowid;

/// The number of rows changed by the most recent INSERT, UPDATE or <a href="http://www.sqlite.org/lang_delete.html#trucateopt">DELETE</a> statement [readonly].
@property (nonatomic, readonly) NSUInteger recentChanges;

/// The total number of rows changed by INSERT, UPDATE and <a href="http://www.sqlite.org/lang_delete.html#trucateopt">DELETE</a> statements [readonly].
@property (nonatomic, readonly) NSUInteger totalChanges;

@end

/**
 ** @brief      A compiled SQL statement.
 ** @details    SQLite compiles an SQL statement into a program for its <a href="http://www.sqlite.org/opcode.html">virtual machine</a>.
 ** @par        Fast Enumeration:
 **             - The fast enumeration (for...in) language feature is used to evaluate a compiled SQL statement (see @ref db_example1).
 **             - Each row of the result set is represented by an object that responds to the `valueForKey:` selector.
 **             - Individual values (conforming to the DBValue protocol) can be accessed by their column name or zero-based column index.
 **             - NOTE: If there is no AS clause, the column name is unspecified, and may change from one release of SQLite to the next.
 **             - An array containing all of the column values can be accessed with a [row valueForKey:@"*"] message.
 **             - See also the DBConnection#executeSQL: and DBStatement#execute convenience methods.
 ** @throw      #DBException if an SQLite error occurs.
 **/
@interface DBStatement : NSObject <NSFastEnumeration> {
@private
    __weak DBConnection *db_connection;
    NSString *db_sql;
    sqlite3_stmt *db_handle;
    NSMutableDictionary *db_row;
    unsigned long db_mutationCount;
}

/**
 ** @brief      Compiles an SQL statement.
 ** @return     An initialized DBStatement object; or `nil` if the input is an empty string or a <a href="http://www.sqlite.org/lang_comment.html">comment</a>.
 ** @param      connection      A connection to an SQLite database.
 ** @param      sql             An SQL statement, using the <a href="http://www.sqlite.org/lang.html">language</a> that SQLite understands.
 **/
- (id)initWithConnection:(DBConnection *)connection SQL:(NSString *)sql;

/**
 ** @brief      Destroys a compiled SQL statement.
 ** @return     `self` so that a nested expression can be used (see the \@finally block of @ref db_example1).
 **/
- (id)close;

/**
 ** @brief      Resets a compiled SQL statement.
 ** @details
 **             - If a fast enumeration is allowed to finish (i.e. without using `break` or `return`), the statement is implicitly reset.
 **             - Likewise, if the DBConnection#executeSQL: or DBStatement#execute convenience methods are used, it isn't necessary to invoke this method.
 **             - Statements that have not been reset or closed may cause subsequent database operations (e.g. COMMIT TRANSACTION) to fail.
 **             - Resetting a statement does not reset its parameters (see the DBStatement#setValue:forKey: method).
 **/
- (void)reset;

/**
 ** @brief      Executes a compiled SQL statement.
 ** @return     The first column of the first row as a `nil`, NSNull, NSNumber, NSString or NSData value (see the DBValue protocol).
 ** @note       To access other columns or rows, use a fast enumeration instead (see @ref db_example1).
 ** @see        DBConnection#executeSQL:
 **/
- (id)execute;

/**
 ** @brief      Binds a value to an SQL parameter.
 ** @param      value           A `nil`, NSNull, NSNumber, NSString or NSData value (see the DBValue protocol).
 ** @param      key             A <a href="http://www.sqlite.org/c3ref/bind_parameter_name.html">parameter name</a> or one-based parameter index.
 **/
- (void)setValue:(id)value forKey:(NSString *)key;

/// The connection used by the initializer [readonly].
@property (nonatomic, readonly, assign) __weak DBConnection *connection;

/// The SQL statement used by the initializer [readonly].
@property (nonatomic, readonly, copy) NSString *sql;

/// The <a href="http://www.sqlite.org/c3ref/stmt.html">sqlite3_stmt</a> handle [readonly].
@property (nonatomic, readonly, assign) sqlite3_stmt *handle;

@end
